package signer

import (
	"crypto"
	"crypto/x509"
	"io"

	"blockchain/bccsp"
	"github.com/pkg/errors"
)

/**
@author:lwj
@date:2022.2.23
*/

// bccspCryptoSigner 是使用crypto.Signer 实现的 BCCSP基础签名工具
type bccspCryptoSigner struct {
	// csp工具
	csp bccsp.BCCSP
	// bccsp中的秘钥
	key bccsp.Key
	// 公钥
	pk interface{}
}

// New返回一个新的基于bccsp的加密。
// 需要给定给定BCCSP实例和秘钥key
func New(csp bccsp.BCCSP, key bccsp.Key) (crypto.Signer, error) {
	// 合法性判断
	//csp 以及key都必须是非空且为非对称钥
	if csp == nil {
		return nil, errors.New("bccsp instance must be different from nil.")
	}
	if key == nil {
		return nil, errors.New("key must be different from nil.")
	}

	if key.Symmetric() {
		return nil, errors.New("key must be asymmetric.")
	}

	// 将公钥作为 bccsp的公钥
	pub, err := key.PublicKey()
	if err != nil {
		return nil, errors.Wrap(err, "failed getting public key")
	}

	raw, err := pub.Bytes()
	// 得到pub不能有任何错误
	// marshall 编组
	if err != nil {
		return nil, errors.Wrap(err, "failed marshalling public key")
	}

	pk, err := x509.ParsePKIXPublicKey(raw)
	if err != nil {
		return nil, errors.Wrap(err, "failed marshalling der to public key")
	}

	return &bccspCryptoSigner{csp, key, pk}, nil
}

// 返回对应于私钥的公钥
func (s *bccspCryptoSigner) Public() crypto.PublicKey {
	return s.pk
}

// 使用私钥去签名,当然有可能是使用rand的。
// 对于一个(EC)DSA 秘钥，这应该是一个序列化的,满足ASN.1的签名结构
//
// 哈希实现了SignerOpts接口，
// 在大多数情况下，可以简单地传入作为opts的哈希函数。
// Sign也可以尝试将断言类型选择为其他类型，以获取算法特定的值。
// 详细信息请参阅每个包中的文档。
//
// 注意如果消息过大,应该将消息进行手动的hash之后再进行签名
func (s *bccspCryptoSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
	return s.csp.Sign(s.key, digest, opts)
}
